library IEEE;
use IEEE.std_logic_1164.all;
use ieee.numeric_std.all;
use work.alu_type.all;

entity dlx_cu is
port (	Clk				: in   std_logic;  -- Clock
		Rst				: in   std_logic;  -- Reset:Active-Low
		-- Instruction Register
		IR_IN			: in   std_logic_vector(31 downto 0);
		-- IF Control Signal
		IR_LATCH_EN		: out std_logic;  -- Instruction Register Latch Enable
		NPC_LATCH_EN	: out std_logic;  -- NextProgramCounter Register Latch Enable
		-- ID Control Signals
		RegA_LATCH_EN	: out std_logic;  -- Register A Latch Enable
		RegB_LATCH_EN      : out std_logic;  -- Register B Latch Enable
		RegIMM_LATCH_EN	: out std_logic;  -- Immediate Register Latch Enable
		-- EX Control Signal
		EQ_COND			: out std_logic;  -- Branch if (not) Equal to Zero
		JUMP_EN			: out std_logic;  -- JUMP Enable Signal for PC input MUX
		ALU_OPCODE		: out TYPE_OP; 
									  -- ALU Operation Code
		MUXA_SEL		: out std_logic;  -- MUX-A Sel
		MUXB_SEL		: out std_logic;  -- MUX-B Sel
		ALU_OUTREG_EN	: out std_logic;  -- ALU Output Register Enable
		-- MEM Control Signals
		DRAM_WE		: out std_logic;  -- Data RAM Write Enable
		LMD_LATCH_EN	: out std_logic;  -- LMD Register Latch Enable
		PC_LATCH_EN		: out std_logic;  -- Program Counte Latch Enable
		WB_MUX_SEL		: out std_logic;  -- Write Back MUX Sel
		RF_WE			: out std_logic   -- Register File Write Enable
	);
end dlx_cu;

architecture dlx_cu_rtl of dlx_cu is
 
constant INSTRUCTIONS_EXECUTION_CYCLES : integer := 5;  -- Instructions Execution                                                  -- Clock Cycles
constant MICROCODE_MEM_SIZE      	     	 : integer := 256;  -- Microcode Memory Size (164 orig.)
constant RELOC_MEM_SIZE             	   	 : integer := 64;  -- Microcode Relocation
constant FUNC_SIZE					 : integer := 11;	-- Func Field Size for R-Type Ops
constant OP_CODE_SIZE					 : integer := 6;		-- Op Code Size
constant ALU_OPC_SIZE					 : integer := 2;		-- ALU Op Code Word Size
constant IR_SIZE						 : integer := 32;	-- Instruction Register Size    
constant CW_SIZE						 : integer := 15;	-- Control Word Size
 
	type mem_array is array (integer range 0 to MICROCODE_MEM_SIZE - 1) of std_logic_vector(CW_SIZE - 1 downto 0);
	type reloc_mem_array is array (0 to RELOC_MEM_SIZE - 1) of unsigned(OP_CODE_SIZE +1 downto 0);

-- All R-Type Instructions are not Relocated (they come from the FUNC register, and their opcode is 
-- multiplied by 4 in order not to interfere with the relocated microcode mem (maybe))

  signal reloc_mem : reloc_mem_array := (
						X"00", 	-- R-Type Instructions
						X"00",
						X"02",	-- J(0x02) -> 0x02
						X"cd",	-- JAL(0x03) -> 0xcd (205)
						X"08",	-- BEQZ(0x04) -> 0x08
						X"de",	-- BNEZ(0x05) ->0xDE (222)
						X"00",
						X"00",
						X"50",	-- ADDI(0x08) -> 0x0E
						X"54",	-- ADDUI(0x09) -> 0x0F
						X"58",	-- SUBI(0x0a) -> 0x10
						X"5c",	-- SUBUI(0x0b) -> 0x11
						X"42",	-- ANDI(0x0c) -> 0x44 (66)
						X"46",	-- ORI(0x0d) -> 0x46
						X"4a",	-- XORI(0x0e) -> 0x48
						X"00",
						X"00",
						X"00",
						X"71",	-- JR(0x12) -> 0x71
						X"6d",	-- JALR(0x13) -> 0x6d (109)
						X"3a",	-- SLLI(0x14) -> 0x3a (58)
						X"fc",	-- NOP(0x15) -> 0x2a
						X"64",	-- SLRI(0x16) -> 0x64 (100)
						X"68",	-- SRAI(0x17) -> 0x68
						X"6e",	-- SEQI(0x18) -> 0x6e
						X"72",	-- SNEI(0x19) -> 0x72
						X"76",	-- SLTI(0x1a) -> 0x76
						X"7a",	-- SGTI(0x1b) -> 0x7a
						X"20",	-- SLEI(0x1c) -> 0x20
						X"24",	-- SGEI(0x1d) -> 0x24
						X"00",
						X"00",
						X"2d", 	-- LB(0x20) -> 0x2d
						X"31",	-- LH(0x21) -> 0x31
						X"00", 
						X"29",	-- LW(0x23) -> 0x29
						X"e3",	-- LBU(0x24) -> 0xe3
						X"da",	-- LHU(0x25) -> 0xda
						X"00",
						X"00",
						X"3e",	-- SB(0x28) -> 0x3e (62)
						X"14",	-- SH(0x29) -> 0x14 (20)
						X"00",
						X"36",  	-- SW(0x2b) -> 0x36
						X"00",
						X"00",
						X"00",
						X"ba",	-- SLTUI(0x3a) -> 0xba (188)
						X"be",	-- SGTUI(0x3b) -> 0xbe
						X"c2",	-- SLEUI(0x3c) -> 0xc2
						X"c6",	-- SGEUI(0x3d) -> 0x
						X"00",
						X"00",
						X"00",
						X"00",
						X"00",
						X"00",
						X"00",
						X"00",
						X"00",
						X"00",
						X"00",
						X"00",
						X"00");

signal microcode : mem_array := (
					"000000000000000", -- RESET 
					"110000000000000", -- (IF)
					"001010000000000", -- (ID) 'J' (0x02)
					"000000111000000", -- (EX)
					"000000000100101", -- (MEM)
					"000000000000010", -- (WB)
					"000000000000000",
					"000000000000000",
					"001010000000000", -- (ID) 'BEQZ' (0x04)
					"000000111000000", -- (EX)
					"000000000100101", -- (MEM)
					"000000000000010", -- (WB)
					"000000000000000",
					"000000000000000",
					"000000000000000",
					"000000000000000",
					"001100000000000", -- (ID) 'SLL' (0x00,0x04)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001110000000000", -- (ID) 'SH' (0x29)
					"000000011000000", -- (EX)
					"000000000010000", -- (MEM)
					"000000000001001", -- (WB)
					"001100000000000", -- (ID) 'SRL' (0x00,0x06)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SRA' (0x00,0x07)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SLEI' (0x1c)				--32
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB) 
					"001010000000000", -- (ID) 'SGEI' (0x1d)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"000000000000000",
					"001010000000000", -- (ID) 'LW' (0x23)
					"000000011000000", -- (EX)
					"000000000001000", -- (MEM)
					"000001000000001", -- (WB)
					"001010000000000", -- (ID) 'LB' (0x20)
					"000000011000000", -- (EX)
					"000000000001000", -- (MEM)
					"000001000000001", -- (WB)
					"001010000000000", -- (ID) 'LH' (0x21)
					"000000011000000", -- (EX)
					"000000000001000", -- (MEM)
					"000001000000001", -- (WB)
					"000000000000000",
					"001110000000000", -- (ID) 'SW' (0x2b)
					"000000011000000", -- (EX)
					"000000000010000", -- (MEM)
					"000000000001001", -- (WB)
					"001010000000000", -- (ID) 'SLLI' (0x14)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)	
					"001110000000000", -- (ID) 'SB' (0x28)
					"000000011000000", -- (EX)
					"000000000010000", -- (MEM)						--64
					"000000000001001", -- (WB)		
					"001010000000000", -- (ID) 'ANDI' (0x0c)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'ORI' (0x0d)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'XORI' (0x0e)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)	
					"000000000000000",
					"000000000000000",
					"001010000000000", -- (ID) 'ADDI' (0x08)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'ADDUI' (0x09)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SUBI' (0x0a)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SUBUI' (0x0b)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"000000000000000",							--96
					"000000000000000",
					"000000000000000",
					"000000000000000",
					"001010000000000", -- (ID) 'SRLI' (0x16)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SRAI' (0x17)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"000000000000000",
					"000000000000000",
					"001010000000000", -- (ID) 'SEQI' (0x18)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SNEI' (0x19)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SGTI' (0x1a)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SLTI' (0x1b)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"000000000000000",
					"000000000000000",
					"001100000000000", -- (ID) 'ADD' (0x00,0x20)			--128
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB) 
					"001100000000000", -- (ID) 'ADDU' (0x00,0x21)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SUB' (0x00,0x22)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SUBU' (0x00,0x23)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'AND' (0x00,0x24)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'OR' (0x00,0x25)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'XOR' (0x00,0x26)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"000000000000000",
					"000000000000000",
					"000000000000000",
					"000000000000000",
					"001100000000000", -- (ID) 'SEQ' (0x00,0x28)			--160
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SNE' (0x00,0x29)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SLT' (0x00,0x2a)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SGT' (0x00,0x2b)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SLE' (0x00,0x2c)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SGE' (0x00,0x2d)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"000000000000000",
					"000000000000000",
					"001010000000000", -- (ID) 'SLTUI' (0x3a)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SGRUI' (0x3b)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)					--192
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SLEUI' (0x3c)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001010000000000", -- (ID) 'SGEUI' (0x3d)
					"000000011000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"000000000000000",
					"000000000000000",
					"000000000000000",
					"001010000000000", -- (ID) 'JAL' (0x03)
					"000000111000000", -- (EX)
					"000000000100101", -- (MEM)
					"000001000000010", -- (WB)
					"001010000000000", -- (ID) 'JALR' (0x13)
					"000000001000000", -- (EX)
					"000000000100101", -- (MEM)
					"000001000000010", -- (WB)
					"001010000000000", -- (ID) 'JR' (0x12)
					"000000001000000", -- (EX)
					"000000000100101", -- (MEM)
					"000000000000010", -- (WB)
					"000000000000000",
					"001010000000000", -- (ID) 'LHU' (0x25)
					"000000011000000", -- (EX)
					"000000000001000", -- (MEM)
					"000001000000001", -- (WB)
					"001010000000000", -- (ID) 'BNEZ' (0x05)
					"000000111000000", -- (EX)
					"000000000000101", -- (MEM)					--224
					"000000000000010", -- (WB)
					"000000000000000",
					"001010000000000", -- (ID) 'LBU' (0x24)
					"000000011000000", -- (EX)
					"000000000001000", -- (MEM)
					"000001000000001", -- (WB)
					"000000000000000",
					"001100000000000", -- (ID) 'SLTU' (0x00,0x3a)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SGTU' (0x00,0x3b)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SLEU' (0x00,0x3c)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"001100000000000", -- (ID) 'SGEU' (0x00,0x3d)
					"000000001000000", -- (EX)
					"000000000000000", -- (MEM)
					"000001000000011", -- (WB)
					"000000000000000",
					"000000000000000",
					"000000000000000",
					"000000000000000",
					"000000000000000", -- (ID) 'NOP' (0x15)
					"000000000000000", -- (EX)
					"000000000000000", -- (MEM)
					"000000000000001"  -- (WB)							--255
				);
	
signal cw : std_logic_vector(CW_SIZE - 1 downto 0);

  signal uPC : integer range 0 to 131072;
  signal ICount : integer range 0 to INSTRUCTIONS_EXECUTION_CYCLES;
  signal OpCode : unsigned(OP_CODE_SIZE -1 downto 0);
  signal OpCode_Reloc : unsigned(OP_CODE_SIZE + 1 downto 0);
  
  constant R_OPCODE : unsigned(OP_CODE_SIZE -1 downto 0) := "000000";
                                                        
  signal func : unsigned(FUNC_SIZE - 1 downto 0);  

begin  -- dlx_cu_rtl

  cw <= microcode(uPC);

  IR_LATCH_EN <= cw(CW_SIZE - 1);
  NPC_LATCH_EN <= cw(CW_SIZE - 2);

  RegA_LATCH_EN <= cw(CW_SIZE - 3);
  RegB_LATCH_EN <= cw(CW_SIZE - 4);
  RegIMM_LATCH_EN <= cw(CW_SIZE - 5);
  RF_WE <= cw(CW_SIZE - 6);

  MUXA_SEL <= cw(CW_SIZE - 7);
  MUXB_SEL <= cw(CW_SIZE - 8);
  ALU_OUTREG_EN <= cw(CW_SIZE - 9);
  EQ_COND <= cw(CW_SIZE - 10);
  DRAM_WE <= cw(CW_SIZE - 11);
  LMD_LATCH_EN <= cw(CW_SIZE - 12);
  JUMP_EN <= cw(CW_SIZE - 13);

  WB_MUX_SEL <= cw(CW_SIZE - 14);

  PC_LATCH_EN <= cw(CW_SIZE - 15);

  OpCode <= unsigned(IR_IN(IR_SIZE -1 downto 26));
  OpCode_Reloc <= reloc_mem(to_integer(unsigned(OpCode)));
  func <= unsigned(IR_IN(FUNC_SIZE - 3 downto 0)) & "00";  -- Multiply func value by 4 (Shift left)

  -- purpose: Generation of ALU OpCode
  -- type   : combinational
  -- inputs : OpCode,func
  -- outputs: ALU_OPCODE
ALU_OP_CODE_P: process (OpCode,func)
begin  -- process ALU_OP_CODE_P
	--ALU_OPCODE <= NOP;
	case OpCode(5 downto 0) is
		when R_OPCODE =>         -- R-Type Instructions
			case to_integer(unsigned(func))is
				when 16 => -- SLL
					ALU_OPCODE <= LLSH;
				when 24 => -- SRL
					ALU_OPCODE <= LRSH;
				when 28 => -- SRA
					ALU_OPCODE <= ARSH;
				when 128 => -- ADD
					ALU_OPCODE <= ADDS;
				when 132=>  -- ADDU
					ALU_OPCODE <= ADDU;
				when 136 => -- SUB
					ALU_OPCODE <= SUBS;        
				when 140 => -- SUBU
					ALU_OPCODE <= SUBS; 
				when 144 => --AND
					ALU_OPCODE <= BIT_AND;	
				when 148 => --OR
					ALU_OPCODE <= BIT_OR;	
				when 152 => --XOR
					ALU_OPCODE <= BIT_XOR;
				when 160 => --SEQ
					ALU_OPCODE <= EQUALS;
				when 164 => --SNE
					ALU_OPCODE <= NOT_EQ;
				when 168 => --SLT
					ALU_OPCODE <= LESS;
				when 172 => --SGT
					ALU_OPCODE <= GREATER;
				when 176 => --SLE
					ALU_OPCODE <= LE_EQ;
				when 180 => --SGE
					ALU_OPCODE <= GR_EQ;
				when 232 => --SLTU
					ALU_OPCODE <= LESS_U;
				when 236 => --SGTU
					ALU_OPCODE <= GREATER_U;
				when 240 => --SLEU
					ALU_OPCODE <= LE_EQ_U;
				when 244 => --SGEU
					ALU_OPCODE <= GR_EQ_U;
				when others =>
					ALU_OPCODE <= NOP;
			end case;
		when "000010" => --J
			ALU_OPCODE <=ADDS;
		when "000011" => --JAL
			ALU_OPCODE <=ADDS;
		when "000100" => --BEQZ
			ALU_OPCODE <=ADDS;
		when "000101" => --BNEZ
			ALU_OPCODE <=ADDS;
		when "001000" => --ADDI
			ALU_OPCODE <= ADDS;
		when "001001" => --ADDUI
			ALU_OPCODE <= ADDU;
		when "001010" => --SUBI
			ALU_OPCODE <= SUBS;
		when "001011" => --SUBUI
			ALU_OPCODE <= SUBU;				
		when "001100" => --ANDI
			ALU_OPCODE <= BIT_AND;	
		when "001101" => --ORI
			ALU_OPCODE <= BIT_OR;	
		when "001110" => --XORI
			ALU_OPCODE <= BIT_XOR;	
		when "010010" => --JR
			ALU_OPCODE <= MOV;	
		when "010011" => --JALR
			ALU_OPCODE <= MOV;	
		when "010100" => --SLLI
			ALU_OPCODE <= LLSH;
		when "010101" => --NOP
			ALU_OPCODE <= NOP;
		when "010110" => --SRLI
			ALU_OPCODE <= LRSH;
		when "010111" => --SRAI
			ALU_OPCODE <= ARSH;
		when "011000" => --SEQI
			ALU_OPCODE <= EQUALS;
		when "011001" => --SNEI
			ALU_OPCODE <= NOT_EQ;
		when "011010" => --SLTI
			ALU_OPCODE <= LESS;
		when "011011" => --SGTI
			ALU_OPCODE <= GREATER;
		when "011100" => --SLEI
			ALU_OPCODE <= LE_EQ;
		when "011101" => --SGEI
			ALU_OPCODE <= GR_EQ;
		when "100000" => --LB
			ALU_OPCODE <= ADDS;
		when "100001" => --LH
			ALU_OPCODE <= ADDS;
		when "100011" => --LW
			ALU_OPCODE <= ADDS;
		when "100100" => --LBU
			ALU_OPCODE <= ADDS;
		when "100101" => --LHU
			ALU_OPCODE <= ADDS;
		when "101000" => --SB
			ALU_OPCODE <= ADDS;
		when "101001" => --SH
			ALU_OPCODE <= ADDS;
		when "101011" => --SW
			ALU_OPCODE <= ADDS;
		when "111010" => --SLTUI
			ALU_OPCODE <= LESS_U;
		when "111011" => --SGTUI
			ALU_OPCODE <= GREATER_U;
		when "111100" => --SLEUI
			ALU_OPCODE <= LE_EQ_U;
		when "111101" => --SGEUI
			ALU_OPCODE <= GR_EQ_U;
		when others => 
			ALU_OPCODE <= NOP;
	end case;
end process ALU_OP_CODE_P;

  -- purpose: Update the uPC value depending on the instruction Op Code
  -- type   : sequential
  -- inputs : Clk, Rst, IR_IN
  -- outputs: CW Control Signals
uPC_Proc: process (Clk, Rst)
begin  -- process uPC_Proc
	if Rst = '1' then                   -- asynchronous reset (active high)
		uPC <= 0;
		ICount <= 0;
	elsif rising_edge(clk) then  -- rising clock edge
		if (ICount < 1) then
			uPC <= 1;                  --performs IF
			ICount <= ICount + 1;
		elsif (ICount < 2) then
			if (OpCode = R_OPCODE) then   --register-to-register op (reads op from func)
				uPC <= to_integer(unsigned(func));
			else
				uPC <= to_integer(unsigned(OpCode_Reloc));   --takes the key (address) for the microcode from the reloc table
			end if;
		ICount <= ICount + 1;
		elsif (ICount < INSTRUCTIONS_EXECUTION_CYCLES) then  --reads the next controlw from the ucode
			uPC <= uPC + 1;
			ICount <= ICount + 1;
		else                                                 --else IF
			ICount <= 1;
			uPC <= 1;
		end if;
	end if;
end process uPC_Proc;

end architecture dlx_cu_rtl;

